# 自写模块
import Read_data
import PCAprocess
import SVRProcess
import LSTMProcess
import write_data
# python自带模块
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
from math import sqrt
import pyecharts.options as opts
from pyecharts.charts import Line
# from pyecharts.globals import ThemeType

class Prediction_main():
    def __init__(self, place=0):
        self.place=place
        
        if self.place==0:  
            self.filename = 'Prediction Graphed/data_jiuzhaigou_final.xls'
            self.placename='九寨沟'
        else:
            self.filename = 'Prediction Graphed/data_siguniangshan.xls'
            self.placename='四姑娘山'
            
        self.all_feature = []
        self.all_flow_label = []
        self.svr_predict = []
        self.svr_flow = []
        self.lstm_feature_weather = []
        self.lstm_ratio = 0.75
        self.svr_ratio = 0.5
        self.final_flow_hat = []
        self.final_flow_real = []
        self.lstm_process = 0

        self.mainProcess()

    # main函数
    def mainProcess(self):
        # 读13个搜索指数
        rd_baidu_feature = Read_data.Read_data(self.filename, 1, 13)
        baidu_feature = rd_baidu_feature.outputdata
        
        #异常处理：降维过程数据为空异常
        try:
            # pca降维
            pca_process = PCAprocess.PCAprocess(baidu_feature, 3, self.placename)
        except :
            pass
        
        pca_data = pca_process.output_data
        # 读其他特征
        rd_other_feature = Read_data.Read_data(self.filename, 14, 14)
        other_feature = rd_other_feature.outputdata
        # 读客流标签数据
        rd_flow_label = Read_data.Read_data(self.filename, 24, 24)
        flow_label = rd_flow_label.outputdata
        # 数据合并
        self.all_feature = np.hstack((pca_data, other_feature))
        self.all_flow_label = flow_label
        # svr处理
        svr_process = SVRProcess.SVRProcess(feature=self.all_feature, label=self.all_flow_label, placename = self.placename, train_ratio=self.svr_ratio, kernel='linear')
        self.svr_predict = svr_process.y_hat_real
        self.svr_flow = svr_process.y_test_real

        # 残差和svr的预测
        excel_redsidu = self.svr_flow - self.svr_predict
        excel_hatflow = self.svr_predict
        excel_redsidu = np.array(excel_redsidu)
        excel_hatflow = np.array(excel_hatflow)
        excel_hatflow = excel_hatflow.reshape((-1, 1))
        excel_redsidu = excel_redsidu.reshape((-1, 1))
        data_1 = np.hstack((excel_redsidu, excel_hatflow))
        write_data.write_data('PredictionResult/'+self.placename+'残差_svr预测图.xls', data_1)

        # lstm数据集生成
        rd_lstm_feature = Read_data.Read_data(self.filename, 15, 23)
        self.lstm_feature_weather = rd_lstm_feature.outputdata
        # 截取特征长度
        len_feature = len(self.svr_flow)
        self.lstm_feature_weather = self.lstm_feature_weather[-len_feature:]

        self.lstm_label_residual = self.svr_flow - self.svr_predict
        # lstm处理
        self.lstm_process = LSTMProcess.LSTMProcess(feature = self.lstm_feature_weather, label=self.lstm_label_residual, placename=self.placename, 
                                                    train_ratio=self.lstm_ratio, epochs=90)
        # 最终评估指标所需数据处理
        self.estimate_dataprocess()
        # 计算并输出评估指标
        self.estimate_final()
        # 画出最终效果图
        self.plot_final()

    # 最终评估指标所需数据处理
    def estimate_dataprocess(self):
        n = len(self.lstm_feature_weather) - 1  # 数据的行数，转监督学习时损失一行
        len_train = self.lstm_process.len_train_number  # 向下取整，训练集长度
        len_test = self.lstm_process.len_test_number  # 测试集长度
        # 得到真正的客流test集
        lstm_flow = self.svr_flow[:]  # 残差剪了头（因为没有第一天的历史客流），所以该客流也要剪头。
        lstm_use_flow = lstm_flow[1:]  # lstm转监督学习函数时会因为NAN损失第一行，再剪头
        lstm_test_flow_real = lstm_use_flow[len_train:]  # 取出真正预测的flow集：0.5，0.5时应该时379
        # 得到最终使用的残差
        residuals_hat = self.lstm_process.y_hat_real  # lstm预测的残差
        # 得到svr预测的客流并且最终使用的test集
        lstm_flow_hat = self.svr_predict[:]
        lstm_use_flow_hat = lstm_flow_hat[1:]
        lstm_test_flow_svrhat = lstm_use_flow_hat[len_train:]

        self.final_flow_real = lstm_test_flow_real
        for i in range(0, len(residuals_hat)):
            self.final_flow_hat.append(residuals_hat[i] + lstm_test_flow_svrhat[i])
        self.final_flow_hat = list(self.final_flow_hat)

    """
    获取绘图数据函数
    输入：两个一维list要按列并列写入文件，一个文件名
    输出：一个xls文件
    """

    def plotData(self, list1, list2, filename):
        plot_list1 = np.array(list1).reshape((-1, 1))
        plot_list2 = np.array(list2).reshape((-1, 1))
        plot_data = np.hstack((plot_list1, plot_list2))
        write_data.write_data(filename, plot_data)

    # 画图
    def plot_final(self):
        r = len(self.final_flow_real) + 1
        plt.plot(np.arange(1, r), self.final_flow_hat, 'r-', label="final_predict")
        plt.plot(np.arange(1, r), self.final_flow_real, '-', label="true_flow")
        self.plotData(self.final_flow_hat, self.final_flow_real, 'PredictionResult/'+self.placename+'最终_预测客流_真实客流_画图数据.xls')
        plt.legend(loc='upper right')

        week_name_list = range(1,len( self.final_flow_hat))
        new=[]
        for i in range(len(week_name_list)):
            new.append(self.final_flow_hat[i][0])
        for i in range(len(week_name_list)):
            new[i] = int(new[i])

        #self.final_flow_hat=np.asarray(self.final_flow_hat)
        
        if self.place==0:
            title="九寨沟客流真实值与预测值对比"
        else:
            title="四姑娘山客流真实值与预测值对比"
        
        (
            # Line(init_opts=opts.InitOpts(theme=ThemeType.DARK, width="1400px", height="600px"))
            Line(init_opts=opts.InitOpts(width="1400px", height="600px"))
                .add_xaxis(xaxis_data=week_name_list)
                .add_yaxis(
                series_name="真实值",
                y_axis=self.final_flow_real,
                markpoint_opts=opts.MarkPointOpts(
                    data=[
                        opts.MarkPointItem(type_="max", name="最大值"),
                        opts.MarkPointItem(type_="min", name="最小值"),
                    ]
                ),
                markline_opts=opts.MarkLineOpts(
                    data=[opts.MarkLineItem(type_="average", name="平均值")]
                ),
            )
                .add_yaxis(
                series_name="预测值",
                y_axis=new,

                markline_opts=opts.MarkLineOpts(
                    data=[
                        opts.MarkLineItem(type_="average", name="平均值"),
                        opts.MarkLineItem(symbol="circle", type_="max", name="最高点"),
                    ]
                ),
            )
                .set_global_opts(
                title_opts=opts.TitleOpts(title=title),
                tooltip_opts=opts.TooltipOpts(trigger="axis"),
                toolbox_opts=opts.ToolboxOpts(is_show=True),
                xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
            )
                .render('resultFile/5. '+title+"折线图.html")
        )

        plt.show()

    # 评估指标
    def estimate_final(self):
        # 对线性核函数模型评估
        print("组合模型决定系数(r方，R_squared)值为：", r2_score(self.final_flow_real, self.final_flow_hat))
        # 计算RMSE
        rmse = sqrt(mean_squared_error(self.final_flow_real, self.final_flow_hat))
        print('组合模型均方根误差(RMSE,Root Mean Square Error): ', rmse)
        print("组合模型均方误差(MSE, mean squared error)为:", mean_squared_error(self.final_flow_real, self.final_flow_hat))
        print("组合模型平均绝对值误差(MAE,mean_absolute_error)为:", mean_absolute_error(self.final_flow_real, self.final_flow_hat))
        print("组合模型平均绝对值误差(MAPE,mean_absolute_error)为:", mean_absolute_error(self.final_flow_real, self.final_flow_hat))


# Prediction_main()
# Prediction_main('data_jiuzhaigou_final.xls')
